#include "Epoll.h"

#include "Channel.h"
#include "Logging.h"
#include "utils.h"

#include <cstdio>
#include <iostream>
#include <memory>
#include <string>
#include <sys/epoll.h>
#include <vector>
using namespace monsoon;
namespace droneswarm
{
    const int EVENTSUM = 4096;
    const int EPOLLWAIT_TIME = 10000;  // epoll_wait的超时时间
    // events_是存放epoll_wait返回的就绪事件的struct epoll_event结构体的容器
    Epoll::Epoll()
        : epollFd_( epoll_create1( EPOLL_CLOEXEC ) )
        , events_( EVENTSUM )
    {
        CondPanic( epollFd_ > 0, "Epoll create failed" );
    }
    Epoll::~Epoll() {}

    //注册新的文件描述符到epoll实例中
    /*
        这个函数的功能：
            1、根据channel的事件类型，设置epoll_event的events成员。
            2、将channel的上层持有者添加到fd2http_中，下标就是该channel的fd。
            3、将channel添加到fd2chan_中，下标就是该channel的fd。
            4、因为是新的channel，所以也是需要初始化lastEvents_的，所以调用EqualAndUpdateLastEvents()函数。
                将当前的events_赋值给lastEvents_。
            5、将当前的channel添加到fd2chan_中进行管理，下标就是该channel的fd.
            6、将当前的channel添加到epoll实例中进行管理。
    */
    void Epoll::epoll_add( SP_Channel request, int timeout )
    {
        int fd = request->get_fd();
        //将request添加到epoll前会将其添加到定时器管理类中，这个类中存放了所有添加到epoll实例中的
        // DdapData的对象，即timerManager_类中会有存放DdapData对象的容器，在每次的loop循环中会定期执行处理到期事件的函数
        //而这个handleExpiredEvent函数会从容器中检查是否具有过期的DdapData对象。
        if ( timeout > 0 )
        {
            add_timer( request, timeout );
            fd2http_[ fd ] = request->getHolder();  //这是第二个持有HttpData对象的智能指针。
        }
        struct epoll_event event;             //创建事件结构体
        event.data.fd = fd;                   //设置事件的文件描述符
        event.events = request->getEvents();  //获取channel的事件类型
        request->EqualAndUpdateLastEvents();  //更新lastEvents_，不管相不相同都更新lastEvents_
        fd2chan_[ fd ] = request;             //将channel添加到fd2chan_中,下标就是该channel的fd。
        if ( epoll_ctl( epollFd_, EPOLL_CTL_ADD, fd, &event ) < 0 )
        {
            perror( "epoll_add error" );
            LOG << "epoll_add error" << '\n';
            fd2chan_[ fd ].reset();  //因为添加失败，所以就需要将上一段代码添加进去的channel进行释放。
        }
    }

    //修改文件描述符状态
    void Epoll::epoll_mod( SP_Channel request, int timeout )
    {
        if ( timeout > 0 )
        {
            add_timer( request, timeout );
        }
        int fd = request->get_fd();
        //如果事件发生变化则更新
        if ( !request->EqualAndUpdateLastEvents() )
        {
            struct epoll_event event;
            event.data.fd = fd;
            event.events = request->getEvents();
            //修改epoll实例中的事件
            if ( epoll_ctl( epollFd_, EPOLL_CTL_MOD, fd, &event ) < 0 )
            {
                perror( "epoll_mod error" );
                LOG << "epoll_mod error" << '\n';
                fd2chan_[ fd ].reset();
            }
        }
    }

    //从epoll实例中删除事件
    void Epoll::epoll_del( SP_Channel request )
    {
        // std::cout << "5:Epoll::epoll_del" << std::endl;
        int fd = request->get_fd();
        struct epoll_event event;
        event.data.fd = fd;
        event.events = request->getEvents();
        //删除epoll实例中的事件
        if ( epoll_ctl( epollFd_, EPOLL_CTL_DEL, fd, &event ) < 0 )
        {
            perror( "epoll_del error" );
            LOG << "epoll_del error" << '\n';
        }
        fd2chan_[ fd ].reset();
        fd2http_[ fd ].reset();
    }

    //返回活跃的事件所属的Channel
    std::vector< SP_Channel > Epoll::poll()
    {
        while ( true )
        {
            int event_count = epoll_wait( epollFd_, &*events_.begin(), events_.size(), EPOLLWAIT_TIME );
            // std::cout << event_count << std::endl;
            if ( event_count < 0 )
            {
                perror( "epoll wait error" );
                LOG << "epoll wait error" << '\n';
            }
            //获得就绪事件的Channel
            std::vector< SP_Channel > req_data = getEventsRequest( event_count );
            //如果就绪事件的数量大于0，则返回就绪事件的Channel
            if ( req_data.size() > 0 )
            {
                return req_data;
            }
        }
    }

    void Epoll::handleExpired() { timerManager_.handleExpiredEvent(); }

    //分发处理函数
    std::vector< std::shared_ptr< Channel > > Epoll::getEventsRequest( int events_num )
    {
        //创建一个vector容器，用来存放活跃的事件所属的Channel
        std::vector< SP_Channel > req_data;
        //传递的events_num说明有多少的活跃事件，并且会将活跃事件的文件描述符存放在events_中
        for ( int i = 0; i < events_num; ++i )
        {
            //获取有事件产生的描述符
            int fd = events_[ i ].data.fd;
            //根据描述符找到对应的Channel
            SP_Channel cur_req = fd2chan_[ fd ];
            //如果cur_req不为空，则说明存在这个Channel，则需要将这个channel的就绪事件修改成检测到的事件。
            if ( cur_req )
            {
                cur_req->set_Revents( events_[ i ].events );
                cur_req->set_Events( 0 );
                req_data.push_back( cur_req );
            }
            else
            {
                LOG << "SP cur_req is invalid!";
            }
        }
        return req_data;
    }

    void Epoll::add_timer( SP_Channel request_data, int timeout )
    {
        std::shared_ptr< DdapData > t =
            request_data->getHolder();  //这里返回的是channel类里面创建的一个HttpData的智能指针。
        if ( t )
        {
            timerManager_.addTimer( t, timeout );  // HttpData对象的持有者+1，为TimeNode。
        }
        else
        {
            LOG << "timer add fail";
        }
    }
}  // namespace droneswarm